﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using TowerDefence_Zombie.Data;
using GameFramework;
using Unity.Burst.CompilerServices;
using System.Linq;

namespace TowerDefence_Zombie
{
    public partial class LevelControl : IReference
    {
        private ProcedureLevel owner;

        private Level level;
        private LevelManager levelManager;

        private EntityLoader entityLoader;

        private DataLevel dataLevel;
        private DataPlayer dataPlayer;
        private DataTower dataTower;
        private DataEnemy dataEnemy;
        private DataSynthesis dataSynthesis;
        private DataPlacement dataPlacement;

        private readonly Dictionary<int, TowerInfo> dicTowerInfo;
        private readonly Dictionary<int, EntityEnemy> dicEntityEnemy;
        private readonly Dictionary<int, SingleTowerPlacementArea> dicPlacementArea;
        private readonly Dictionary<int, TowerInfo> dicPlacementTowerInfo;

        private readonly Dictionary<int, EntityTowerPreview> dicPreviewTower;

        public LevelControl()
        {
            dicTowerInfo = new Dictionary<int, TowerInfo>();
            dicEntityEnemy = new Dictionary<int, EntityEnemy>();
            dicPlacementArea = new Dictionary<int, SingleTowerPlacementArea>();
            dicPlacementTowerInfo = new Dictionary<int, TowerInfo>();
            dicPreviewTower = new Dictionary<int, EntityTowerPreview>();
        }

        public void OnEnter()
        {
            entityLoader = EntityLoader.Create(this);
            dataLevel = GameEntry.Data.GetData<DataLevel>();
            dataPlayer = GameEntry.Data.GetData<DataPlayer>();
            dataTower = GameEntry.Data.GetData<DataTower>();
            dataEnemy = GameEntry.Data.GetData<DataEnemy>();
            dataSynthesis = GameEntry.Data.GetData<DataSynthesis>();
            dataPlacement = GameEntry.Data.GetData<DataPlacement>();

            RefreshLevel();

            GameEntry.UI.OpenUIForm(EnumUIForm.UILevelMainInfoForm);
            entityLoader.ShowEntity<EntityPlayer>(EnumEntity.Player
                , null, EntityData.Create(level.PlayerPosition, level.PlayerQuaternion));

            foreach (var item in dataTower.dicTowerData)
            {
                ShowPreviewTower(item.Value, 0);
            }

            //——初始化场景内炮塔数据
            var plcaementAreas = UnityEngine.Object.FindObjectsOfType<SingleTowerPlacementArea>();
            for (int i = 0; i < plcaementAreas.Length; i++)
            {
                int areaId = plcaementAreas[i].AreaId;
                dicPlacementArea.Add(areaId, plcaementAreas[i]);
            }

            foreach (var placementDataItem in dataPlacement.DicPlacementAreaData)
            {
                if (dicPlacementArea.TryGetValue(placementDataItem.Key, out var area))
                {
                    area.SetLockState(!placementDataItem.Value.UnLockState);
                    if (area.IsLock == false && placementDataItem.Value.TowerId > 0)
                    {
                        var towerDataTable = dataTower.GetTowerData(placementDataItem.Value.TowerId);
                        var towerLevel = placementDataItem.Value.TowerLevel;
                        var areaPos = area.WorldToGrid(area.transform.position, towerDataTable.Dimensions);
                        var fitPos = area.GridToWorld(areaPos, towerDataTable.Dimensions);
                        var fitRot = area.transform.rotation;
                        CreateTower(towerDataTable, towerLevel, area, areaPos, fitPos, fitRot, false);
                    }
                }
                else Log.Error($"未找到场景内对应的放置位 id == {placementDataItem.Key}");
            }
        }

        public void Update(float elapseSeconds, float realElapseSeconds, bool isGuidePause, bool isCanOperaSceneTower)
        {
            if (level == null)
                return;

            if (dataLevel.LevelState != EnumLevelState.Normal)
                return;

            if (!level.Finish && !isGuidePause)
                level.ProcessLevel(elapseSeconds, realElapseSeconds);

            ProcessInputOperate(isCanOperaSceneTower);
        }

        public void ShowPreviewTower(TowerData towerData, int towerLevel)
        {
            entityLoader.ShowEntity<EntityTowerPreview>(towerData.PreviewEntityId, (entity) =>
                {
                    TowerLevelData towerLevelData = towerData.GetTowerLevelData(towerLevel);
                    if (towerLevelData == null)
                    {
                        Log.Error("Tower '{0}' Level '{1}' data is null.", towerData.Name, towerLevel);
                    }

                    EntityDataRadiusVisualiser entityDataRadiusVisualiser = EntityDataRadiusVisualiser.Create(towerLevelData.Range);
                    entityLoader.ShowEntity<EntityRadiusVisualizer>(EnumEntity.RadiusVisualiser, (entityRadiusVisualizer) =>
                    {
                        GameEntry.Entity.AttachEntity(entityRadiusVisualizer, entity);
                        var radiusVisualiser = entityRadiusVisualizer.Logic as EntityRadiusVisualizer;
                        (entity.Logic as EntityTowerPreview).OnVisualizerCreat(radiusVisualiser);
                    },
                    entityDataRadiusVisualiser);

                    entity.Logic.Visible = false;
                    dicPreviewTower.Add(towerData.Id, entity.Logic as EntityTowerPreview);
                }
                , EntityDataTowerPreview.Create(towerData, towerLevel));
        }

        public void UnlockPlacementArea(int areaId)
        {
            if (dicPlacementArea.TryGetValue(areaId, out var area))
            {
                area.SetLockState(false);
            }
        }

        public void HighlightPlacementArea(int areaId, bool highlightState)
        {
            if (dicPlacementArea.TryGetValue(areaId, out var area))
            {
                area.SetHighlightState(highlightState);
            }
        }

        public bool CheckPreviewTowerState(int towerId)
        {
            return dicPreviewTower.TryGetValue(towerId, out var towerPreview)
                && towerPreview.IsVisualizerComplete;
        }

        public bool TryGetPreviewTower(int towerId, out EntityTowerPreview towerPreview)
        {
            return dicPreviewTower.TryGetValue(towerId, out towerPreview);
        }

        public void HideAllPreviewTower()
        {
            foreach (var item in dicPreviewTower)
            {
                if (item.Value.Visible)
                    item.Value.Visible = false;
            }
        }

        public void OnPlacementAreaChange(int areaID, int towerId, int towerLevel, bool isUpgrade)
        {
            if (dicPlacementArea.TryGetValue(areaID, out var area))
            {
                if (dicPlacementTowerInfo.TryGetValue(areaID, out var towerInfo))
                {
                    HideTower(towerInfo.Tower.SerialId);
                }

                if (towerId > 0)
                {
                    var towerDataTable = dataTower.GetTowerData(towerId);
                    var areaPos = area.WorldToGrid(area.transform.position, towerDataTable.Dimensions);
                    var fitPos = area.GridToWorld(areaPos, towerDataTable.Dimensions);
                    var fitRot = area.transform.rotation;
                    CreateTower(towerDataTable, towerLevel, area, areaPos, fitPos, fitRot, isUpgrade);
                }
            }
            else Log.Error($"OnAreaDataChange : 炮塔放置位无效，未找到场景内放置位对象。 id=={areaID}");
        }

        public void CreateTower(TowerData towerData
            , int towerLevel
            , IPlacementArea placementArea
            , IntVector2 placeGrid
            , Vector3 position
            , Quaternion rotation
            , bool isUpgrade)
        {
            if (towerData == null) return;

            Tower tower = dataTower.CreateTower(towerData.Id, towerLevel, isUpgrade);

            if (tower == null)
            {
                Log.Error("Create tower fail,Tower data id is '{0}'.", towerData.Id);
                return;
            }

            entityLoader.ShowEntity(towerData.EntityId, TypeUtility.GetEntityType(tower.Type),
            (entity) =>
            {
                placementArea.Occupy(placeGrid, towerData.Dimensions, towerData.Id);
                EntityTowerBase entityTowerBase = entity.Logic as EntityTowerBase;
                TowerInfo towerInfo = TowerInfo.Create(tower, entityTowerBase, placementArea, placeGrid);
                dicTowerInfo.Add(tower.SerialId, towerInfo);
                dicPlacementTowerInfo.Add(placementArea.AreaId, towerInfo);
            }
            , EntityDataTower.Create(tower, position, rotation));
        }

        public void SetTowerVisible(int towerSerialId, bool state)
        {
            if (!dicTowerInfo.TryGetValue(towerSerialId, out var towerInfo))
                return;

            if (towerInfo.EntityTower.Visible != state)
            {
                towerInfo.EntityTower.Visible = state;
                towerInfo.EntityTower.entityLogicTowerLevel.ResetTowerState(state);
            }
        }

        public void HideTower(int towerSerialId)
        {
            if (!dicTowerInfo.ContainsKey(towerSerialId))
                return;

            TowerInfo towerInfo = dicTowerInfo[towerSerialId];
            IPlacementArea towerArea = towerInfo.PlacementArea;
            int towerAreaId = towerArea.AreaId;
            entityLoader.HideEntity(dicTowerInfo[towerSerialId].EntityTower.Entity);
            towerArea.Clear(towerInfo.PlaceGrid, towerInfo.Tower.Dimensions);
            dataTower.DestroyTower(towerInfo.Tower);
            dicTowerInfo.Remove(towerSerialId);
            dicPlacementTowerInfo.Remove(towerAreaId);
            ReferencePool.Release(towerInfo);
        }

        private void HideAllTower()
        {
            List<int> towerSerialIds = new List<int>(dicTowerInfo.Keys);
            for (int i = 0; i < towerSerialIds.Count; i++)
            {
                HideTower(towerSerialIds[i]);
            }

            foreach (var item in dicPreviewTower)
            {
                entityLoader.HideEntity(item.Value.Entity);
            }
            dicPreviewTower.Clear();
        }

        public void SpawnEnemy(int enemyId, int CoinNum)
        {
            EnemyData enemyData = dataEnemy.GetEnemyData(enemyId);

            if (enemyData == null)
            {
                Log.Error("Can not get enemy data by id '{0}'.", enemyId);
                return;
            }

            entityLoader.ShowEntity(enemyData.EntityId, TypeUtility.GetEntityType(enemyData.Type),
                (entity) =>
                {
                    dicEntityEnemy.Add(entity.Id, (EntityEnemy)entity.Logic);
                },
                EntityDataEnemy.Create(
                    enemyData,
                    levelManager.GetLevelPath(),
                    levelManager.GetStartPathNode().position - new Vector3(0, 0.2f, 0),
                    Quaternion.identity,
                    CoinNum));
        }

        public void HideEnemyEntity(int serialId)
        {
            if (!dicEntityEnemy.ContainsKey(serialId))
            {
                Log.Error("Can't find enemy entity('serial id:{0}') ", serialId);
                return;
            }

            entityLoader.HideEntity(serialId);
            dicEntityEnemy.Remove(serialId);

            if (level.waveCount <= 1 && (level.CurrentWave == null || level.CurrentWave.waveElementsCount <= 0) && dicEntityEnemy.Count <= 0)
            {
                GameEntry.Event.Fire(this, GameEndEventArgs.Create(EnumGameOverType.Success));
            }
        }

        public void SpawmBeDamagedEnemy(int enemyId, Vector3 position, float damage, bool isPoison)
        {
            entityLoader.ShowEntity(1076, TypeUtility.GetEntityType("DamageFloatText"),
                (entity) => { }, DamageWordDataEnemy.Create(position, damage, isPoison));
        }

        private void HideAllEnemyEntity()
        {
            foreach (var item in dicEntityEnemy.Values)
            {
                entityLoader.HideEntity(item.Entity);
            }
            dicEntityEnemy.Clear();
        }

        public void ShowEntity(int entityId, Type entityType, Action<Entity> showSuccess, EntityData entityData)
        {
            entityLoader.ShowEntity(entityId, entityType, (entity) =>
            {
                //这里处理一下，如果暂停前调用ShowEntity，暂停后才成功加载出来调用Entity的OnShow的话，那这个Entity是没被执行到IPause的逻辑的，这里在ShowEntity成功的回调下补充个调用IPause的逻辑
                if (dataLevel.LevelState == EnumLevelState.Pause)
                {
                    IPause iPause = entity.Logic as IPause;
                    if (iPause != null)
                    {
                        iPause.Pause();
                    }
                }
                if (showSuccess != null)
                    showSuccess(entity);
            }, entityData);
        }

        public void HideEntity(int entityId)
        {
            entityLoader.HideEntity(entityId);
        }

        public void StartWave()
        {
            if (dataLevel.LevelState == EnumLevelState.Pause)
            {
                dataLevel.ChangeLevelState(EnumLevelState.Normal);
            }
            level.StartWave();
            GameEntry.Event.Fire(this, LazyWaveStartEventArgs.Create());
        }

        public void Pause()
        {
            foreach (var entity in entityLoader.GetAllEntities())
            {
                IPause iPause = entity.Logic as IPause;
                if (iPause != null)
                    iPause.Pause();
            }
        }

        public void Resume()
        {
            foreach (var entity in entityLoader.GetAllEntities())
            {
                IPause iPause = entity.Logic as IPause;
                if (iPause != null)
                    iPause.Resume();
            }
        }

        public void Gameover(EnumGameOverType enumGameOverType, bool isForce = false)
        {
            HideAllEnemyEntity();
            var curLevelIndex = dataPlayer.GetCurBattleLevel();
            dataLevel.GetLevelData(curLevelIndex, out var curLevelData);

            Dictionary<string, string> dic = new()
            {
                { "U", UPTrace.UPTraceApi.getUserId() },
                { "G", curLevelIndex.ToString() },
            };
            string result = "1";
            if (enumGameOverType == EnumGameOverType.Fail)
            {
                result = "0";
            }
            dic.Add("S", result);

            string manualResult = "1";
            if (isForce)
            {
                manualResult = "2";
            }
            dic.Add("O", manualResult);

            dic.Add("T", Mathf.RoundToInt(level.LevelTimer).ToString());

            UPTrace.UPTraceApi.traceDictionary("T09", dic);

            Log.Info($"关卡通关结果:{enumGameOverType} 当前关卡{curLevelIndex} BossLevel:{curLevelData.isBossLevel}");
            if (enumGameOverType == EnumGameOverType.Success)
            {
                if (curLevelData.isBossLevel)
                {
                    dataPlayer.SignBossLevelChallengeResult(true);
                }

                //强制切换不走以下宝箱等逻辑
                if (isForce)
                {
                    SwitchNextLevel(isAutoStart: true, force: isForce);
                    return;
                }

                if (curLevelIndex == 19)
                {
                    var fightUI = (UILevelMainInfoForm)GameEntry.UI.GetUIForm(EnumUIForm.UILevelMainInfoForm);
                    if (fightUI != null)
                    {
                        fightUI.OpenCarSkill();
                    }
                }

                GameEntry.Sound.PlaySound(EnumSound.SuccessMusic, null);
                if (dataPlayer.GetBossLevelChallengeResult() == false)
                {
                    RefreshLevel();
                    StartWave();
                    return;
                }

                bool isHaveTreasureBox = level != null
                    && curLevelData.TreasureBoxId > 0
                    && dataPlayer.GetLevelIsGainedTreasure(curLevelIndex) == false;
                if (isHaveTreasureBox)
                {
                    var uITreasureFormOpenParam = UITreasureFormOpenParam.Create(curLevelData.TreasureBoxData);
                    GameEntry.UI.OpenUIForm(EnumUIForm.UITreasureForm, uITreasureFormOpenParam);
                    dataPlayer.AddTreasureGainLevel(curLevelIndex);
                }
                SwitchNextLevel(isAutoStart: !isHaveTreasureBox, false);
            }
            else
            {
                GameEntry.UI.OpenUIForm(EnumUIForm.UIGameOverForm);
                if (curLevelData.isBossLevel)  //如果当前是boss关卡的话回退一关
                {
                    dataPlayer.SignBossLevelChallengeResult(false);
                    SwitchBackLevel(false);
                }
                else RefreshLevel();

                GameEntry.Event.Fire(this, LevelFailEventArgs.Create(curLevelIndex));
            }
        }

        private void SwitchNextLevel(bool isAutoStart, bool force)
        {
            int curLevel = dataPlayer.GetCurBattleLevel();
            if (dataPlayer.NextLevel(out var nextLevel, out var nextLevelData))
            {
                if (GameEntry.Data.GetData<DataLevel>().GetLevelData(curLevel, out var curLevelData)
                    && curLevelData.AllowTowers.Length != nextLevelData.AllowTowers.Length)
                {
                    var diff1 = curLevelData.AllowTowers.Except(nextLevelData.AllowTowers);
                    var diff2 = nextLevelData.AllowTowers.Except(curLevelData.AllowTowers);
                    var allDiffs = diff1.Union(diff2).ToList();
                    if (force == false && allDiffs.Count > 0
                        && GameEntry.Data.GetData<DataTower>().GetTowerData(allDiffs[0], out var towerData))
                    {
                        GameEntry.UI.OpenUIForm(EnumUIForm.UIUnlockNewPlayer, new UnlockNewPlayer()
                        {
                            IconResPath = towerData.BigIcon,
                            Name = towerData.Name
                        });
                    }
                }
                Log.Info($"切换至下一关{nextLevel}成功");
            }
            else Log.Info($"切换至下一关{nextLevel}失败");

            RefreshLevel();

            if (isAutoStart)
            {
                StartWave();
            }

            GameEntry.Event.Fire(this, LevelSuccessEventArgs.Create(nextLevel));
        }

        private void SwitchBackLevel(bool isAutoStart)
        {
            if (dataPlayer.RollbackLevel(out var backLevel, out _))
            {
                Log.Info($"回滚至上一关{backLevel}成功");
            }
            else Log.Info($"回滚至上一关{backLevel}失败");

            RefreshLevel();

            if (isAutoStart)
            {
                StartWave();
            }
        }

        private void RefreshLevel()
        {
            if (level != null)
                ReferencePool.Release(level);

            var curBattleLevel = dataPlayer.GetCurBattleLevel();
            dataLevel.GetLevelData(curBattleLevel, out var levelData);
            level = Level.Create(levelData);
            dataLevel.SwitchLevel(curBattleLevel, level);

            Dictionary<string, string> dic = new()
            {
                { "U", UPTrace.UPTraceApi.getUserId() },
                { "G", curBattleLevel.ToString() },
                { "L", "0" },
                { "H1", "None_001" },
                { "H2", "None_002" },
                { "H3", "None_003" },
            };
            UPTrace.UPTraceApi.traceDictionary("T08", dic);
        }

        public void Quick()
        {
            HideAllEnemyEntity();
            HideAllTower();
            entityLoader.HideAllEntity();
        }

        public void ReleaseCarSkill()
        {
            levelManager.carSkillEntity.ReleaseSkills();
        }

        public static LevelControl Create(LevelManager levelPathManager, ProcedureLevel procedureLevel)
        {
            LevelControl levelControl = ReferencePool.Acquire<LevelControl>();
            levelControl.levelManager = levelPathManager;
            levelControl.owner = procedureLevel;
            return levelControl;
        }

        public void Clear()
        {
            level = null;
            levelManager = null;

            if (entityLoader != null)
                ReferencePool.Release(entityLoader);

            entityLoader = null;

            dataPlayer = null;
            dataTower = null;

            previewTowerData = null;
            previewTowerEntityLogic = null;

            dicTowerInfo.Clear();
            dicEntityEnemy.Clear();
        }
    }
}
